[Swift 공식문서 읽기]The Basics

안녕하세요. 엘림입니다🙇🏻‍♀️

오늘부터 Swift 공식 문서를 정독해보려고 합니다!!
가끔 필요할때만 들어가서 봤는데, 한번은 쭉 읽어보는게 좋을 것 같아서 시작하게 되었어욥.

제 스타일대로 정리했으니 추가적으로 더 필요한 정보는
공식문서 링크를 눌러 확인해주세용!

자, 그럼 시작해볼까요

이 글은 공부하면서 작성한 글이기 때문에 잘못된 정보가 있을 수 있습니다.🥺
금방 잊어버릴... 미래의 저에게 다시 알려주기 위한 글이다보니
혹시라도 틀린 부분이 있다면, 댓글로 친절하게 알려주시길 부탁드립니다.🙏


The Basics

Swift에는 Int, Double, Float, Bool, String 등의 자료형이 있으며,
Array, Set, Dictionary의 3가지 컬렉션 유형이 있습니다.

이름으로 식별하는 변수를 사용하여 값을 저장하고 참조합니다.
또한 변경할 필요가 없는 값을 상수로 사용하여, 코드를 더 안전하고 명확하게 만들 수 있습니다.

친숙한 유형 외에도 튜플과 같은 고급 유형 또한 사용됩니다.
튜플을 사용하면 값 그룹을 만들고 전달할 수 있으며, 함수의 여러 값을 단일 복합 값으로 반환할 수도 있습니다.

Swift는 값의 부재를 처리하는 선택적 유형을 도입합니다. 이것은 Optional 타입으로 값이 있고, 이것은 x와 같다. 또는 값이 전혀 없다. 중에 선택할 수 있습니다.
옵셔널을 사용하는 것은 Objective-C의 nil을 포인터와 함께 사용하는 것과 비슷합니다. 하지만 옵셔널은 클래스뿐만 아니라 모든 유형에서 작동하며, 훨씬 안전하고 표현력이 높으며 Swift의 가장 강력한 핵심 기능이라고 할 수 있습니다.

Swift는 type-safe 언어입니다. 즉, 언어는 값의 유형을 명확하게 하는데 도움을 줍니다.
다른 유형별로 전달되는 것을 막으며, 옵셔널과 옵셔널이 아닌 값 사이에서 값이 전달 되는 것 또한 막을 수 있습니다.
이러한 안전성은 개발 프로세스에서 빨리 오류를 포착하고 수정하는데 도움이 됩니다.

상수 및 변수

상수와 변수는 사용하기 전에 선언되어야하고, 상수는 let으로, 변수는 var로 선언합니다.

let maximumNumberOfLoginAttempts = 10 // 최대값은 변경되지 않으므로 상수
var currentLoginAttempt = 0 // 로그인 시도 횟수는 변경되므로 변수
var x = 0.0, y = 0.0, z = 0.0 // 쉼표로 구분하여 한줄로 선언도 가능


var friendlyWelcome = "Hello!"
friendlyWelcome = "Bonjour!"
// friendlyWelcome is now "Bonjour!"
// 변수는 값을 변경할 수 있습니다.

let languageName = "Swift"
languageName = "Swift++"
// This is a compile-time error: languageName cannot be changed.
// 변수와 달리 상수 값은 설정 후에 변경할 수 없습니다. 
// 그렇게 하려고 하면 코드가 컴파일될 때 오류로 보고됩니다.

공식문서에서는 값이 변경될 일이 없다면, 항상 let을 사용하라고 권하고 있습니다!!

타입 어노테이션(명시)

var welcomeMessage: String
var red, green, blue: Double

타입 어노테이션을 사용하면, 유형을 명확히 할 수 있습니다.
하지만 정의된 시점에서 상수 또는 변수에 대한 초기값을 제공하면 Swift는 거의 항상 유형을 유추할 수 있기 때문에, 실제로 명시하는 경우는 드뭅니다.
다만, 위처럼 초기값이 제공되지 않은 경우에는 유추되지 않으므로 명시합니다.

네이밍

let π = 3.14159
let 你好 = "你好世界"
let 🐶🐮 = "dogcow"

유니코드 문자를 포함하여, 거의 모든 문자가 포함될 수 있습니다.
단 공백문자, 수학 기호, 화살표, 전용 유니코드 스칼라 값, 선 및 상자 그리기는 불가능합니다. 숫자가 포함되는 것은 가능하지만, 숫자로 시작하는 것은 불가능합니다.


💡 수학기호는 위와 같이 +, -, %, / 등을 이야기 하는 것 같습니다. 공식문서 예시의 π는 수학기호가 아닌것으로?!

특정 유형의 상수 혹은 변수로 선언한 후에 동일한 이름으로 다시 선언하거나,
다른 유형의 값을 저장하도록 변경할 수 없습니다!
또한 상수를 변수로, 변수를 상수로 변경하는 것도 불가능합니다.

예약된 Swift키워드와 동일한 이름을 부여해야하는 경우에는 역따옴표(`)로 묶어 사용할 수 있습니다. 그러나 되도록 사용하지 않는 것을 권장하고 있습니다!!

출력

print(_:separator:terminator:)함수를 사용하여 현재 값을 콘솔에 인쇄할 수 있습니다.
separator의 기본값은 " ", 즉 공백문자 입니다.
terminator의 기본값은 "\n", 즉 줄 바꿈 입니다.

let greeting = "hi~"
let introducing = "I'm lina"

print(greeting, introducing)
// hi~ I'm lina (<- 줄바꿈 있음)

print(greeting, introducing, separator: "!")
// hi~!I'm lina (<- 줄바꿈 있음)

print(greeting, introducing, separator: "!", terminator: ":)")
// hi~!I'm lina:) (<- 줄바꿈 없음!)

정수

정수는 42와 -32 같은 숫자를 말합니다. 부호가 있거나(양수, 음수, 0) 없을(양수, 0) 수 있습니다.
Swift는 8, 16, 32, 64비트 형식의 부호가 있는 정수와, 부호가 없는 정수를 제공합니다. (Int8, UInt16 ...)

정수 경계

let minValue = UInt8.min  // minValue is equal to 0, and is of type UInt8
let maxValue = UInt8.max  // maxValue is equal to 255, and is of type UInt8

min, max를 사용하여 각 정수 유형의 최소값 및 최대값에 접근할 수 있습니다.

정수

대부분의 경우, 사용할 특정 크기의 정수를 선택할 필요가 없습니다.
Swift는 현재 플랫폼의 기본 단어 크기와 동일한 크기를 가진 추가 정수 유형을 제공합니다.

  • 32비트 플랫폼에서 Int는 Int32와 크기가 같습니다.
  • 64비트 플랫폼에서 Int는 Int64와 크기가 같습니다.

특정 크기의 정수로 작업해야하는 경우가 아니라면, Int로 사용하는 것을 추천합니다!
32비트 플랫폼에서도 -2,147,483,648 ~ 2,147,483,647 사이의 모든 값을 저장할 수 있으며, 이는 충분히 큰 범위입니다.

💡 iPhone 5S가 첫번째 64-bit 아이폰이며, iOS 11부터는 64-bit만 지원하기로 했다고 합니다!

UInt

UInt 또한 현재 플랫폼의 기본 단어 크기와 동일한 크기를 갖는 unsigned integer 유형을 제공합니다.

  • 32비트 플랫폼에서 UInt는 UInt32와 크기가 같습니다.
  • 64비트 플랫폼에서 UInt는 UInt64와 크기가 같습니다.

부동 소수점

Swift는 두 가지 부호 있는 부동 소수점 숫자 유형을 제공합니다.

  • Double: 64비트 부동 소수점 숫자를 나타냅니다.
  • Float: 32비트 부동 소수점 숫자를 나타냅니다.

Double의 정밀도는 15자리 이상의 소수점 이하 자릿수인 반면 Float의 정밀도는 소수점 이하 6자리입니다. 사용할 적절한 부동 소수점 유형은 코드에서 작업해야 하는 값의 특성과 범위에 따라 다릅니다. 타입을 추론해야 하는 경우에는 Double이 선택됩니다.

타입 안정성 및 추론

Swift는 타입이 안전한 언어입니다. 값 유형을 명확하게 하도록 권장하고, 코드상의 실수로라도 다른 타입에 전달할 수 없습니다.
컴파일시에 타입 검사를 실행하고, 일치하지 않는 유형을 오류로 표시합니다.
타입 검사는 오류를 방지하는데 도움을 주는 것이지, 선언하는 모든 상수 및 변수의 유형을 지정해야 한다는 뜻이 아닙니다.

let meaningOfLife = 42
// meaningOfLife is inferred to be of type Int

let pi = 3.14159
// pi is inferred to be of type Double
// Swift는 부동 소수점 숫자의 유형을 유추할 때 항상 (Float 보다는)Double를 선택 합니다.

let anotherPi = 3 + 0.14159
// anotherPi is also inferred to be of type Double
// 정수와 부동 소수점을 결합하면 (부동 소수점의 유형)Double이 유추됩니다.

숫자 리터럴

let decimalInteger = 17           // 10진수 17
let binaryInteger = 0b10001       // 2진수 17 - 0b 접두사 
let octalInteger = 0o21           // 8진수 17 - 0o 접두사
let hexadecimalInteger = 0x11     // 16진수 17 - 0x 접두사

e와 p

10진수의 경우

  • 1.25e2는 1.25 x 10^2 또는 125.0.
  • 1.25e-2는 1.25 x 10^-2 또는 0.0125.

16진수의 경우

  • 0xFp2는 15 x 2^2 또는 60.0.
  • 0xFp-2는 15 x 2^-2 또는 3.75.
let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0
// 세 상수의 값은 동일합니다.

_와 0

숫자 리터럴에는 읽기 쉽게 언더바 및 0이 추가될 수 있습니다.
두 형식 모두 리터럴 값에 영향을 주지 않습니다.

let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1

숫자 유형 변환

음수가 아닌 것으로 알려진 경우에도 코드의 모든 경우에 Int를 사용할 것을 권장합니다.
기본 정수 유형을 사용한다는 것은, 코드에서 즉시 정수인 변수와 상수가 상호 운용이 가능하고 정수 리터럴 값에 대해 추론된 유형과 일치한다는 것을 의미합니다.
다른 정수 유형은 외부 소스에서 명시적으로 크기 조정된 데이터나 성능, 메모리 사용 또는 기타 필요한 최적화를 위해 당면한 작업에 특별히 필요한 경우에만 사용하십시오.

정수 변환

저장할 수 있는 숫자의 범위는 타입마다 다릅니다.

let cannotBeNegative: UInt8 = -1
// UInt8 음수를 저장할 수 없으므로 에러출력!

let tooBig: Int8 = Int8.max + 1
// Int8의 최대값 보다 큰 값을 저장할 수 없으므로 에러출력!

let twoThousand: UInt16 = 2_000
let one: UInt8 = 1
let twoThousandAndOne = twoThousand + UInt16(one)
// 원래는 더할 수 없지만, 타입을 변환했으므로 가능.
// UInt16에 UInt8을 허용하는 이니셜라이저가 존재함.

정수 및 부동 소수점 변환

정수와 부동 소수점 간의 변환은 명시적으로 이루어져야 합니다.

let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine
// pi equals 3.14159, and is inferred to be of type Double

let integerPi = Int(pi)
// integerPi equals 3, and is inferred to be of type Int

부동 소수점 값을 이러한 방식으로 변환하면, 소수점은 항상 잘려나갑니다.
(ex. Int(4.75) = 4, Int(-3.9) = -3)

Type Aliases

유형 별칭은 기존 유형의 대체 이름을 정의합니다.
원래 이름으로 사용할 수 있는 모든 위치에서 별칭을 대신 사용할 수 있습니다.

typealias AudioSample = UInt16
var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound is now 0

Booleans

Swift에서는 1과 0이 true와 false를 나타내지 않습니다.

let i = 1
if i {
    // this example will not compile, and will report an error
}

let i = 1
if i == 1 {
    // this example will compile successfully
}

Tuples

튜플은 여러 값을 단일 복합 값으로 그룹화 합니다.
튜플 내의 값은 어떤 타입이든 상관 없으며, 서로 같을 필요도 없습니다.

let http404Error = (404, "Not Found")
// http404Error is of type (Int, String)

/// 튜플의 각 요소에 이름을 지어줄 수 있고, index로 접근도 가능함

let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
// Prints "The status code is 404"
print("The status message is \(statusMessage)")
// Prints "The status message is Not Found"

let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")
// Prints "The status code is 404"

print("The status code is \(http404Error.0)")
// Prints "The status code is 404"
print("The status message is \(http404Error.1)")
// Prints "The status message is Not Found"

let http200Status = (statusCode: 200, description: "OK")
print("The status code is \(http200Status.statusCode)")
// Prints "The status code is 200"
print("The status message is \(http200Status.description)")
// Prints "The status message is OK"

Optionals

값이 없을 수 있는 상황에서 사용합니다.
옵셔널에는 값이 있고 해당 값에 접근하기 위해 언래핑을 하거나, 값이 전혀 없는 두 가지 경우가 있습니다.
이 개념은 C나 Object-C에는 존재하지 없습니다. 비슷한 개념인 nil의 경우에는 객체에만 작동하며 기본 타입 등에는 적용되지 않기때문에, Swift의 옵셔널은 모든 유형에 대한 값이 없음을 나타낼 수 있는 장치라고 볼 수 있겠습니다.

let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber is inferred to be of type "Int?", or "optional Int"

nil

var serverResponseCode: Int? = 404
serverResponseCode = nil

var nonoptionalValue: Int
nonoptionalValue = nil // error! 
// optional이 아니면 nil을 할당할 수 없음.

var surveyAnswer: String?
// surveyAnswer is automatically set to nil

if문과 강제 언래핑

let convertedNumber = Int("123")

if convertedNumber != nil {
    print("convertedNumber contains some integer value.")
}
// Prints "convertedNumber contains some integer value."

if convertedNumber != nil {
    print("convertedNumber has an integer value of \(convertedNumber!).")
}
// Prints "convertedNumber has an integer value of 123."

if문을 사용하여 옵셔널에 값이 포함되어 있는지 확인할 수 있습니다.
옵셔널에 값이 포함되어있다고 확신하면 !(강제 언래핑)를 통하여 기본 값에 접근할 수 있습니다.
단, 존재하지 않는 옵셔널 값에 !로 접근을 하게되면 런타임 오류가 납니다. 그러므로 강제로 풀기 전에는 항상 확인을 해줘야 합니다.

옵셔널 바인딩

옵셔널에 값이 있는지 확인하여 임시 상수나, 변수로 사용할 수 있도록 하는 방법입니다.
옵셔널 바인딩으로 생성된 상수나 변수는 명령문의 본문 내에서만 사용할 수 있으며, 이와 대조적으로 guard 명령문을 차후에 살펴볼 예정입니다.

if let actualNumber = Int(possibleNumber) {
    print("The string \"\(possibleNumber)\" has an integer value of \(actualNumber)")
} else {
    print("The string \"\(possibleNumber)\" couldn't be converted to an integer")
}
// Prints "The string "123" has an integer value of 123"


if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
    print("\(firstNumber) < \(secondNumber) < 100")
}
// Prints "4 < 42 < 100"

if let firstNumber = Int("4") {
    if let secondNumber = Int("42") {
        if firstNumber < secondNumber && secondNumber < 100 {
            print("\(firstNumber) < \(secondNumber) < 100")
        }
    }
}
// Prints "4 < 42 < 100"

쉼표로 조건을 구분하는 경우, 하나라도 조건이 false인 경우 연관된 모든 조건이 false인 것으로 간주됩니다. 또한 변수로 선언해도 전혀 문제가 없습니다.

💡 쉼표와 &&의 차이
쉼표는 condition을 이어 붙이는 용도(condition-list를 만드는 스위프트 문법의 종류)로 앞 뒤에 condition이 붙습니다.
&&는 두개의 boolean expression을 파라미터로 받는 논리 연산자로 앞 뒤에 boolean expression이 붙습니다.
condition은 expression, availability-condition, case-condition, optional-binding-condition의 4가지 중의 하나를 나타냅니다.

if let a = someOpt, let b = someOtherOpt {} // works
if let a = someOpt && let b = someOtherOpt {} // error

첫번째는 optional-binding-condition 두 개를 콤마로 붙인 condition-list로 잘 표현이 되었지만,
두번째는 논리 연산자에 필요한 두 개의 인자가 bool expression이 아니므로, 연산자를 잘못 썼다는 에러가 발생하게 됩니다. ("hello" && "world"가 안되는 것과 같은 이유)

if 1 == 1, 2 == 2 {} //works
if 1 == 1 && 2 == 2 {} //works

둘다 기능적으로는 동일하게 동작하지만, 첫번째는 condition(== 연산자를 쓴 expression)이 두 개이고, 두번째는 condition(&& 연산자를 쓴 expression)이 하나라고 생각할 수 있습니다.
결론적으로, expression은 condition이지만 condition은 expression이 아니기 때문에 이런 차이가 발생하게 됩니다.
추가로, 위에 condition을 이어붙인다고 했는데, 이렇게 이어붙여진 condition들을 condition-list라고 부릅니다. while, if, guard 문에는 condition-list를 쓰지만 repeat-while 문에서는 condition만 쓸 수 있습니다.

출처 링크와 추가 읽을 거리
Short-circuit Evaluation, Side Effect
Is “&&” equal to “,”?

암시적 언래핑

때로는 프로그래밍 구조에서 해당 값이 처음 설정된 후에, 옵셔널이 항상 값을 갖는 경우가 있습니다. 이러한 경우 항상 값을 가지고 있다고 안전하게 가정할 수 있기 때문에, 접근할때마다 값을 확인하고 언래핑하는 것이 불필요해집니다.
이럴때 암시적 옵셔널인 !를 사용하면 좋습니다.
압시적 언래핑 옵셔널은 처음 정의된 직후 옵셔널의 값이 존재하는 것으로 확인되고, 그 이후의 모든 지점에 존재한다고 확실히 가정할 수 있을때 유용합니다.

let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // requires an exclamation point

let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // no need for an exclamation point
let optionalString = assumedString
// The type of optionalString is "String?" and assumedString isn't force-unwrapped.

암시적 언래핑 옵셔널은 일반적인 옵셔널이라고 할 수도 있지만, 접근할때마다 언래핑 할 필요가 없기 때문에 위와 같이 옵셔널이 아닌 것 처럼 사용할 수도 있습니다.

오류 처리

오류 처리를 사용하여 프로그램 실행 중에 발생할 수 있는 오류 조건에 응답합니다.
값의 유무를 사용하여 함수의 성공 또는 실패를 전달할 수 있는 옵셔널과 달리,
오류 처리를 사용하면 실패의 근본 원인을 확인하고, 필요한 경우 오류를 프로그램의 다른 부분으로 전파할 수 있습니다.

함수에서는 throws 키워드를 사용하여 오류가 발생할 수 있음을 나타내고,
이 함수를 호출할 때 try 키워드를 표현식 앞에 추가합니다.

func makeASandwich() throws {
    // ...
}

do {
    try makeASandwich()
    eatASandwich()
} catch SandwichError.outOfCleanDishes {
    washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
    buyGroceries(ingredients)
}

Assertions and Preconditions

assertions와 preconditions는 런타임에 발생하는 검사입니다.
추가 코드를 실행하기 전에, 필수 조건이 충족되었는지 확인하는 데 사용합니다.
두 검사가의 결과가 true라면 코드 실행은 평소처럼 진행되고, false라면 코드 실행 및 앱이 종료됩니다.
assert는 디버그 빌드에서만 확인이되지만 precondition은 프로덕션 빌드에서도 반영됩니다. 즉, asser는 프로덕션 성능에 영향을 주지 않고, 개발 프로세스 중에 원하는 만큼 사용할 수 있습니다.

assert

assert(_:_:file:line:)는 조건식의 결과가 false인 경우 메세지를 표시합니다.

let age = -3
assert(age >= 0, "A person's age can't be less than zero.")
// This assertion fails because -3 isn't >= 0.

assert(age >= 0) // 메세지 생략도 가능

/// 이미 실패한 경우 아래와 같이 표현할 수 도 있음
if age > 10 {
    print("You can ride the roller-coaster or the ferris wheel.")
} else if age >= 0 {
    print("You can ride the ferris wheel.")
} else {
    assertionFailure("A person's age can't be less than zero.")
}

precondition

precondition(_:_:file:line:)는 조건이 거짓일 가능성이 있을 때마다 전제조건을 사용하지만, 코드가 계속 실행되려면 반드시 조건을 참으로 만족시켜야 합니다.
assertFailure처럼 preconditionFailure(_:file:line:)함수를 호출하여 실패가 발생했음을 나타낼 수도 있습니다.

참고로 컴파일 시, -0unchecked로 컴파일 하면 precondition을 체크하지 않습니다.
하지만 fatalError는 발생하는 즉시 실행이 항상 중지되므로 주의해야합니다.

기타

주석

// This is a comment.

/* This is also a comment
but is written over multiple lines. */

/* This is the start of the first multiline comment.
 /* This is the second, nested multiline comment. */
This is the end of the first multiline comment. */

///* */이 사용 가능하며, 중첩 주석도 가능합니다.

세미콜론

세미콜론은 작성하지 않습니다.
다만, 원한다면 작성할 수 있으며
한줄의 여러 개의 별도 명령문이 있을 경우에는 사용합니다.

let cat = "🐱"; print(cat)
// Prints "🐱"

오늘은 스위프트 공식문서에서 첫번째 챕터인 The basics를 읽어보았습니다~
다음에는 Basic Operatiors를 읽어보도록 하겠습니다!

감사합니다🙇🏻‍♀️

참고

https://appleinsider.com/articles/17/03/15/over-187000-apps-could-become-obsolete-with-apples-64-bit-only-ios-11
https://forums.macrumors.com/threads/is-the-apple-watch-series-3-s3-64-bit.2095262/
https://soojin.ro/blog/swift-comma-vs-and-operator

좋은 웹페이지 즐겨찾기